package com.jfl.core.aspect;

import java.lang.reflect.Method;
import java.util.Map;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSONObject;
import com.jfl.annotation.Log;
import com.jfl.core.utils.ShiroUtils;
import com.jfl.enums.OperationResultEnum;
import com.jfl.model.SysLog;
import com.jfl.model.SysUser;
import com.jfl.monitor.SysLogService;
import com.jfl.utils.AddressUtils;
import com.jfl.utils.DateUtil;
import com.jfl.utils.ServletUtil;

import eu.bitwalker.useragentutils.UserAgent;

/**
 * 操作日志
 * 
 * @author 赵炎
 * @version [V1.00, 2018年12月29日]
 * @since V1.00
 */
@Aspect
@Component
public class LogAspect
{
    private static final Logger LOG = LoggerFactory.getLogger(LogAspect.class);
    
    private ThreadLocal<Long> startTime = new ThreadLocal<Long>();
    
    @Reference(version = "${jfl.version}")
    private SysLogService sysLogService;
    
    @Value("${jfl.address.enable}")
    private boolean addressEnable;
    
    /**
     * 配置日志切点
     */
    @Pointcut("@annotation(com.jfl.annotation.Log)")
    public void logPointcut()
    {
    }
    
    @Before("logPointcut()")
    public void before()
    {
        startTime.set(DateUtil.getCurrentDate().getTime());
    }
    
    /**
     * 
     * @param joinPoint
     */
    @AfterReturning("logPointcut()")
    public void doBefore(JoinPoint joinPoint)
    {
        handleLog(joinPoint, null);
    }
    
    @AfterThrowing(value = "logPointcut()", throwing = "e")
    public void doAfter(JoinPoint joinPoint, Exception e)
    {
        handleLog(joinPoint, e);
    }
    
    private void handleLog(JoinPoint joinPoint, Exception e)
    {
        Log controllerLog = getAnnotation(joinPoint);
        if (controllerLog == null)
        {
            return;
        }
        SysLog log = new SysLog();
        SysUser currentUser = ShiroUtils.getCurrentUser();
        String ip = ServletUtil.getRequest().getRemoteHost();
        if(currentUser == null)
        {
            log.setCreateBy(1L);
            log.setUpdateBy(1L);
            log.setDeptName("");
            log.setOperateName("");
        }
        else
        {
            log.setUpdateBy(currentUser.getId());
            log.setCreateBy(currentUser.getId());
            log.setOperateName(currentUser.getUserName());
            log.setDeptName(currentUser.getDeptName());
        }

        UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtil.getRequest().getHeader("User-Agent"));
        log.setBrowser(userAgent.getBrowser().getName() + " " + userAgent.getBrowserVersion().getVersion());
        log.setOperateOs(userAgent.getOperatingSystem().getName());
        log.setCreateTime(DateUtil.getCurrentDate());
        log.setUpdateTime(DateUtil.getCurrentDate());
        log.setOperateIp(ip);
       
        if(addressEnable) 
        {
            String location = AddressUtils.getRealAddressByIP(ip);
            log.setOperateLocation(location);
        }
        log.setOperateUrl(ServletUtil.getRequest().getRequestURI());
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        log.setMethod(className + "." + methodName + "()");
        String operation = controllerLog.value();
        log.setOperateType(controllerLog.type().ordinal());
        log.setOperation(operation);
        log.setStatus(OperationResultEnum.SUCCESS.ordinal());
        log.setModule(controllerLog.module().getModule());
        Map<String,String[]> reqParam = ServletUtil.getRequest().getParameterMap();
        log.setReqParam(JSONObject.toJSONString(reqParam));
        
        if (e != null)
        {
            log.setStatus(OperationResultEnum.FAIL.ordinal());
            // 过长街截取部分异常信息
            log.setErrorMsg(e.getMessage().length() > 2000 ? e.getMessage().substring(0, 2000): e.getMessage());
        }
        // 请求耗时
        log.setCost(DateUtil.getCurrentDate().getTime() - startTime.get());
        try
        {
            this.sysLogService.save(log);
        }
        catch (Exception ex)
        {
            log.setStatus(OperationResultEnum.FAIL.ordinal());
            this.sysLogService.save(log);
            LOG.error("记录日志异常", ex);
        }
    }
    
    /**
     * 获取controller注解
     * 
     * @param joinPoint
     * @return
     */
    private Log getAnnotation(JoinPoint joinPoint)
    {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature)signature;
        Method method = methodSignature.getMethod();
        if (method != null)
        {
            return method.getAnnotation(Log.class);
        }
        return null;
    }
}
